/*
 * SPDX-FileCopyrightText: 2025 Espressif Systems (Shanghai) CO LTD
 *
 * SPDX-License-Identifier: Unlicense OR CC0-1.0
 */
/* ULP riscv DS18B20 1wire temperature sensor example

   This example code is in the Public Domain (or CC0 licensed, at your option.)

   Unless required by applicable law or agreed to in writing, this
   software is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
   CONDITIONS OF ANY KIND, either express or implied.
*/

#include <stdio.h>
#include "freertos/FreeRTOS.h"
#include "soc/rtc_periph.h"
#include "driver/gpio.h"
#include "driver/rtc_io.h"
#include "esp_sleep.h"
#include "ulp_riscv.h"
#include "ulp_main.h"

extern const uint8_t ulp_main_bin_start[] asm("_binary_ulp_main_bin_start");
extern const uint8_t ulp_main_bin_end[]   asm("_binary_ulp_main_bin_end");

/* Pulses will be generated by HP core, and detected by ULP core on this same pin */
#define GPIO_PULSE_IO GPIO_NUM_1

/* Number of pulses to be generated by HP core */
#define HP_CORE_PULSE_COUNT 500000

/* Delay between two pulses (less value = less delay) */
#if CONFIG_IDF_TARGET_ESP32S2
#define HP_CORE_PULSE_DELAY 29    /* ESP32S2 has slower RTC clock, so it needs longer delay. */
#elif CONFIG_IDF_TARGET_ESP32S3
#define HP_CORE_PULSE_DELAY 6    /* ESP32S3 has faster RTC clock, so it can count pulses at higher speed. */
#else
#error "This example is only supported on ESP32-S2 and ESP32-S3"
#endif

/* NOTE: HP_CORE_PULSE_DELAY has been pre-calculated for each target to achieve maximum frequency
 * For ESP32-S2, with HP_CORE_PULSE_DELAY = 27, maximum pulse frequency is ~120 KHz
 * For ESP32-S3, with HP_CORE_PULSE_DELAY = 5, maximum pulse frequency is ~250 KHz
 * In the above defined macro, some buffer margin has been kept to ensure it works on different devices
 * and different conditions. User can try reducing the delay value further to achieve higher pulse frequency
 */

static void init_ulp_program(void)
{
    esp_err_t err = ulp_riscv_load_binary(ulp_main_bin_start, (ulp_main_bin_end - ulp_main_bin_start));
    ESP_ERROR_CHECK(err);

    /* The first argument is the period index, which is not used by the ULP-RISC-V timer
     * The second argument is the period in microseconds, which gives a wakeup time period of: 20ms
     */
    ulp_set_wakeup_period(0, 20000);

    ulp_edge_limit = (uint32_t)(HP_CORE_PULSE_COUNT << 1);

    /* Start the program */
    err = ulp_riscv_run();
    ESP_ERROR_CHECK(err);
}

static void generate_pulses()
{
    for (uint32_t i = 0; i < HP_CORE_PULSE_COUNT; i++) {
        /* Generate a pulse by setting the GPIO high and low with a delay in between */
        rtc_gpio_set_level(GPIO_PULSE_IO, 1);
        /* Volatile ensures below loop is not optimized by compiler, as it provides blocking delay */
        for (volatile uint32_t j = 0; j < HP_CORE_PULSE_DELAY; j++);
        rtc_gpio_set_level(GPIO_PULSE_IO, 0);
        for (volatile uint32_t j = 0; j < HP_CORE_PULSE_DELAY; j++);
    }
}

void app_main(void)
{
    /* If user is using USB-serial-jtag then idf monitor needs some time to
    *  re-connect to the USB port. We wait 1 sec here to allow for it to make the reconnection
    *  before we print anything. Otherwise the chip will go back to sleep again before the user
    *  has time to monitor any output.
    */
    vTaskDelay(pdMS_TO_TICKS(1000));

    /* Initialize selected GPIO as RTC IO, enable input, disable pullup and pulldown */
    rtc_gpio_init(GPIO_PULSE_IO);
    rtc_gpio_set_direction(GPIO_PULSE_IO, RTC_GPIO_MODE_INPUT_OUTPUT);
    rtc_gpio_pulldown_dis(GPIO_PULSE_IO);
    rtc_gpio_pullup_dis(GPIO_PULSE_IO);

    /* Initialize ULP core */
    init_ulp_program();

    /* Small delay to ensure the ULP is ready to capture pulses */
    vTaskDelay(100);
    printf("Start generating pulses\n");
    generate_pulses();
    printf("Stop generating pulses\n");

    /* Small delay to ensure the ULP pulse counting is complete */
    vTaskDelay(100);

    /* Printing the count summary */
    printf("Number of pulses generated by HP Core: %d\n", HP_CORE_PULSE_COUNT);
    if ((ulp_edge_count / 2) == HP_CORE_PULSE_COUNT) {
        printf("SUCCESS: ULP core was able to count all the pulses correctly, " \
               "try reducing HP_CORE_PULSE_DELAY to generate pulses faster" \
               " and see if ULP core can still count the pulses at that speed.\n");
    } else {
        printf("FAILURE: ULP core couldn't count all the pulses correctly, " \
               "try increasing HP_CORE_PULSE_DELAY to generate slower pulses " \
               "and see if ULP core can still count the pulses at that speed. %ld\n", ulp_edge_count / 2);
    }

    printf("\nEntering deep sleep\n");
    /* Entering deep sleep */
    esp_deep_sleep_start();
}
